/**
 * @file command.c
 * @brief A Simple Console Command Implementation
 *
 * @version Fixed
 * @author yemit@witsi.cn
 * @date 2011-06-17
 */
#include <string.h>
#include <stdio.h>
#include "console.h"

enum ParseState {
        PS_WHITESPACE,
        PS_TOKEN,
        PS_STRING,
        PS_ESCAPE
};
static enum ParseState stackedState;
void parseargs(char *argstr, int *argc_p, char **argv, char** resid)
{
        int argc = 0;
        char c;
        enum ParseState lastState = PS_WHITESPACE;

        /* tokenize the argstr */
        while ((c = *argstr) != 0) {
                enum ParseState newState;

                if (c == ';' && lastState != PS_STRING && lastState != PS_ESCAPE)
                        break;

                if (lastState == PS_ESCAPE) {
                        newState = stackedState;
                } else if (lastState == PS_STRING) {
                        if (c == '"') {
                                newState = PS_WHITESPACE;
                                *argstr = 0;
                        } else {
                                newState = PS_STRING;
                        }
                } else if ((c == ' ') || (c == '\t')) {
                        /* whitespace character */
                        *argstr = 0;
                        newState = PS_WHITESPACE;
                } else if (c == '"') {
                        newState = PS_STRING;
                        *argstr++ = 0;
                        argv[argc++] = argstr;
                } else if (c == '\\') {
                        stackedState = lastState;
                        newState = PS_ESCAPE;
                } else {
                        /* token */
                        if (lastState == PS_WHITESPACE) {
                                argv[argc++] = argstr;
                        }
                        newState = PS_TOKEN;
                }

                lastState = newState;
                argstr++;
        }

        argv[argc] = NULL;
        if (argc_p != NULL)
                *argc_p = argc;

        if (*argstr == ';') {
                *argstr++ = '\0';
        }
        *resid = argstr;
}

void unparseargs(char *argstr, int argc, const char **argv)
{
        int i;
        for (i = 0; i < argc; i++) {
                if (argv[i] != NULL) {
                        strcat(argstr, " ");
                        strcat(argstr, argv[i]);
                }
        }
}

extern command_t __command_entry_start[], __command_entry_end[];

command_t *find_cmd(const char *cmdname)
{
        command_t *cmd;

        if (cmdname == NULL) return NULL;

        for (cmd = __command_entry_start;
                        cmd < __command_entry_end; cmd ++) {

                if (strcmp(cmd->name, cmdname) == 0) {
                        return cmd;
                }
        }
        return NULL;
}

void exec_cmd(int argc, const char **argv)
{
        command_t *cmd = find_cmd(argv[0]);

        if (cmd == NULL) {
                printf("Could not found '%s' command, type 'help'\n", argv[0]);
                return;
        }
        cmd->cmdfunc(argc, argv);
}

void exec_string(char *buf)
{
        int argc;
        char *argv[128];
        char *resid;

        while (*buf) {
                memset(argv, 0, sizeof(argv));
                parseargs(buf, &argc, argv, &resid);
                if (argc > 0)
                        exec_cmd(argc, (const char **)argv);
                buf = resid;
        }
}

void command_help(int argc, const char **argv)
{
        command_t *cmd;

        /* help <command>. invoke <command> with 'help' as an argument */
        if (argc == 2) {
                if (strncmp(argv[1], "help", strlen(argv[1])) == 0) {
                        printf("Are you kidding?\n");
                        return;
                }
                argv[0] = argv[1];
                argv[1] = "help";
                exec_cmd(argc, argv);
                return;
        }

        printf("Usage:\n");
        for (cmd = __command_entry_start;
                        cmd < __command_entry_end; cmd ++) {
                printf("   %s\n", cmd->helpstr);
        }
}

const command_t help_cmd __command_entry = {
        "help",
        command_help,
        "help [{cmds}]  -- Help about help?"
};
